using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace Rogue
{
    public class InputManager
    {
        public static InputManager Instance = new InputManager();

        public delegate void OnKeyPressedHandler(Object a_Sender, KeyEventArgs a_KeyEventArgs);
        public event OnKeyPressedHandler OnKeyPressed;

        public delegate void OnGamePadHandler(Object a_Sender, GamePadEventArgs a_Args);
        public event OnGamePadHandler OnGamePad;

        private GamePadState m_LastGamePadState;
        private MouseState m_LastMouseState;

        private KeyboardState m_LastKeyState;
        private double m_nTimeTillKeyRepeat = 0;
        private readonly double m_nRepeatInterval = 0.05;
        private readonly double m_nRepeatDelay = 0.3;
        private Keys m_RepeatKey = Keys.None;
        private Keys[] m_aNewKeys;

        public InputManager()
        {
            m_LastKeyState = Keyboard.GetState();
            m_LastGamePadState = GamePad.GetState(PlayerIndex.One);
            m_LastMouseState = Mouse.GetState();
        }

        public void Update(GameTime a_dt)
        {
            KeyboardState CurKeyState = Keyboard.GetState();
            GamePadState CurGamePadState = GamePad.GetState(PlayerIndex.One);
            MouseState CurMouseState = Mouse.GetState();
            
            Keys[] a_NewKeys = GetNewKeys(CurKeyState, m_LastKeyState);
            m_aNewKeys = a_NewKeys;
            if (a_NewKeys != null)
            {
                for (int i = 0; i < a_NewKeys.Length; i++)
                {
                    KeyEventArgs KeyArgs = new KeyEventArgs(
                        a_NewKeys[i],
                        CurKeyState.IsKeyDown(Keys.LeftAlt) || CurKeyState.IsKeyDown(Keys.RightAlt),
                        CurKeyState.IsKeyDown(Keys.LeftControl) || CurKeyState.IsKeyDown(Keys.RightControl),
                        CurKeyState.IsKeyDown(Keys.LeftShift) || CurKeyState.IsKeyDown(Keys.RightShift),
                        CurKeyState.IsKeyDown(Keys.CapsLock),
                        CurKeyState.IsKeyDown(Keys.NumLock),
                        CurKeyState.IsKeyDown(Keys.Scroll),
                        false
                        );

                    if (OnKeyPressed != null)
                        OnKeyPressed(this, KeyArgs);
                }
            }

            // Key repeating
            if (a_NewKeys != null && a_NewKeys.Length > 0)
            {
                if (a_NewKeys.Length == 1)
                {
                    if (!m_LastKeyState.IsKeyDown(a_NewKeys[0]))
                    {
                        m_RepeatKey = a_NewKeys[0];
                        m_nTimeTillKeyRepeat = m_nRepeatDelay;
                    }
                }
                else
                {
                    m_RepeatKey = Keys.None;
                }
            }
            else
            {
                if (m_RepeatKey != Keys.None && m_LastKeyState.IsKeyDown(m_RepeatKey))
                {
                    m_nTimeTillKeyRepeat -= a_dt.ElapsedGameTime.Milliseconds / 1000.0f;
                    if (m_nTimeTillKeyRepeat < 0)
                    {
                        KeyEventArgs KeyArgs = new KeyEventArgs(
                            m_RepeatKey,
                            CurKeyState.IsKeyDown(Keys.LeftAlt) || CurKeyState.IsKeyDown(Keys.RightAlt),
                            CurKeyState.IsKeyDown(Keys.LeftControl) || CurKeyState.IsKeyDown(Keys.RightControl),
                            CurKeyState.IsKeyDown(Keys.LeftShift) || CurKeyState.IsKeyDown(Keys.RightShift),
                            CurKeyState.IsKeyDown(Keys.CapsLock),
                            CurKeyState.IsKeyDown(Keys.NumLock),
                            CurKeyState.IsKeyDown(Keys.Scroll),
                            true
                            );
                        
                        if (OnKeyPressed != null)
                            OnKeyPressed(this, KeyArgs);

                        m_nTimeTillKeyRepeat = m_nRepeatInterval;
                    }
                }
            }

            // controller input
            if (CurGamePadState.IsConnected)
            {
                // TODO: also send the input if we sent any input last frame!

                bool anyLeftStick = Math.Abs(CurGamePadState.ThumbSticks.Left.X) > 0.05f ||
                                    Math.Abs(CurGamePadState.ThumbSticks.Left.Y) > 0.05f;

                bool anyDPad = CurGamePadState.DPad.Up == ButtonState.Pressed ||
                                CurGamePadState.DPad.Down == ButtonState.Pressed ||
                                CurGamePadState.DPad.Left == ButtonState.Pressed ||
                                CurGamePadState.DPad.Right == ButtonState.Pressed;

                if (anyLeftStick || anyDPad)
                {
                    if (OnGamePad != null)
                    {
                        GamePadEventArgs args = new GamePadEventArgs(CurGamePadState);
                        OnGamePad(this, args);
                    }
                }
            }

            m_LastKeyState = CurKeyState;
            m_LastGamePadState = CurGamePadState;
            m_LastMouseState = CurMouseState;
        }

        public Keys[] GetNewKeys()
        {
            return m_aNewKeys;
        }

        protected Keys[] GetNewKeys(KeyboardState a_CurState, KeyboardState a_LastState)
        {
            Keys[] CurrentKeys = a_CurState.GetPressedKeys();
            Keys[] LastKeys = a_LastState.GetPressedKeys();
            
            if (CurrentKeys.Length > 0 && LastKeys.Length == 0)
                return CurrentKeys;

            if (CurrentKeys.Length == 0 && LastKeys.Length == 0)
                return null;

            List<Keys> NewKeyList = new List<Keys>();
            bool bIsNew = true;
            foreach(Keys CurrentKey in CurrentKeys)
            {
                foreach (Keys LastKey in LastKeys)
                {
                    if (CurrentKey == LastKey)
                        bIsNew = false;
                }

                if (bIsNew)
                    NewKeyList.Add(CurrentKey);

                bIsNew = true;
            }

            return NewKeyList.ToArray();
        }

        public class GamePadEventArgs : EventArgs
        {
            public GamePadEventArgs(GamePadState a_State)
            {
                m_CurGamePadState = a_State;
            }

            public GamePadState m_CurGamePadState;
        }

        public class KeyEventArgs : EventArgs
        {
            public bool CapsLock;
            public bool NumLock;
            public bool ScrollLock;

            public bool Alt;
            public bool Control;
            public bool Shift;

            public bool Repeating;
            public char Character;
            public readonly Keys Key;

            public KeyEventArgs(Keys a_Key, bool a_bAlt, bool a_bControl, bool a_bShift, bool a_bCapsLock, bool a_bNumLock, bool a_bScrollLock, bool a_bRepeating)
            {
                Key = a_Key;
                Alt = a_bAlt;
                Control = a_bControl;
                Shift = a_bShift;
                CapsLock = a_bCapsLock;
                NumLock = a_bNumLock;
                ScrollLock = a_bScrollLock;
                Repeating = a_bRepeating;

                Character = InputManager.TranslateChar(a_Key, a_bShift, a_bCapsLock, a_bNumLock);
            }
        }

        public static char TranslateChar(Keys a_Key, bool a_bShift, bool a_bCapsLock, bool a_bNumLock)
        {
	        switch (a_Key)
	        {
		        case Keys.A: return TranslateAlphabetic('a', a_bShift, a_bCapsLock);
		        case Keys.B: return TranslateAlphabetic('b', a_bShift, a_bCapsLock);
		        case Keys.C: return TranslateAlphabetic('c', a_bShift, a_bCapsLock);
		        case Keys.D: return TranslateAlphabetic('d', a_bShift, a_bCapsLock);
		        case Keys.E: return TranslateAlphabetic('e', a_bShift, a_bCapsLock);
		        case Keys.F: return TranslateAlphabetic('f', a_bShift, a_bCapsLock);
		        case Keys.G: return TranslateAlphabetic('g', a_bShift, a_bCapsLock);
		        case Keys.H: return TranslateAlphabetic('h', a_bShift, a_bCapsLock);
		        case Keys.I: return TranslateAlphabetic('i', a_bShift, a_bCapsLock);
		        case Keys.J: return TranslateAlphabetic('j', a_bShift, a_bCapsLock);
		        case Keys.K: return TranslateAlphabetic('k', a_bShift, a_bCapsLock);
		        case Keys.L: return TranslateAlphabetic('l', a_bShift, a_bCapsLock);
		        case Keys.M: return TranslateAlphabetic('m', a_bShift, a_bCapsLock);
		        case Keys.N: return TranslateAlphabetic('n', a_bShift, a_bCapsLock);
		        case Keys.O: return TranslateAlphabetic('o', a_bShift, a_bCapsLock);
		        case Keys.P: return TranslateAlphabetic('p', a_bShift, a_bCapsLock);
		        case Keys.Q: return TranslateAlphabetic('q', a_bShift, a_bCapsLock);
		        case Keys.R: return TranslateAlphabetic('r', a_bShift, a_bCapsLock);
		        case Keys.S: return TranslateAlphabetic('s', a_bShift, a_bCapsLock);
		        case Keys.T: return TranslateAlphabetic('t', a_bShift, a_bCapsLock);
		        case Keys.U: return TranslateAlphabetic('u', a_bShift, a_bCapsLock);
		        case Keys.V: return TranslateAlphabetic('v', a_bShift, a_bCapsLock);
		        case Keys.W: return TranslateAlphabetic('w', a_bShift, a_bCapsLock);
		        case Keys.X: return TranslateAlphabetic('x', a_bShift, a_bCapsLock);
		        case Keys.Y: return TranslateAlphabetic('y', a_bShift, a_bCapsLock);
		        case Keys.Z: return TranslateAlphabetic('z', a_bShift, a_bCapsLock);
    			
		        case Keys.D0: return (a_bShift) ? ')' : '0';
		        case Keys.D1: return (a_bShift) ? '!' : '1';
		        case Keys.D2: return (a_bShift) ? '@' : '2';
		        case Keys.D3: return (a_bShift) ? '#' : '3';
		        case Keys.D4: return (a_bShift) ? '$' : '4';
		        case Keys.D5: return (a_bShift) ? '%' : '5';
		        case Keys.D6: return (a_bShift) ? '^' : '6';
		        case Keys.D7: return (a_bShift) ? '&' : '7';
		        case Keys.D8: return (a_bShift) ? '*' : '8';
		        case Keys.D9: return (a_bShift) ? '(' : '9';
    			
		        case Keys.Add:      return '+';
		        case Keys.Divide:   return '/';
		        case Keys.Multiply: return '*';
		        case Keys.Subtract: return '-';

		        case Keys.Space: return ' ';
		        case Keys.Tab:   return '\t';
                case Keys.Enter: return '\n';

		        case Keys.Decimal: if (a_bNumLock && !a_bShift) return '.'; break;
		        case Keys.NumPad0: if (a_bNumLock && !a_bShift) return '0'; break;
		        case Keys.NumPad1: if (a_bNumLock && !a_bShift) return '1'; break;
		        case Keys.NumPad2: if (a_bNumLock && !a_bShift) return '2'; break;
		        case Keys.NumPad3: if (a_bNumLock && !a_bShift) return '3'; break;
		        case Keys.NumPad4: if (a_bNumLock && !a_bShift) return '4'; break;
		        case Keys.NumPad5: if (a_bNumLock && !a_bShift) return '5'; break;
		        case Keys.NumPad6: if (a_bNumLock && !a_bShift) return '6'; break;
		        case Keys.NumPad7: if (a_bNumLock && !a_bShift) return '7'; break;
		        case Keys.NumPad8: if (a_bNumLock && !a_bShift) return '8'; break;
		        case Keys.NumPad9: if (a_bNumLock && !a_bShift) return '9'; break;
    			
		        case Keys.OemBackslash:     return a_bShift ? '|' : '\\';
		        case Keys.OemCloseBrackets: return a_bShift ? '}' : ']'; 
		        case Keys.OemComma:         return a_bShift ? '<' : ','; 
		        case Keys.OemMinus:         return a_bShift ? '_' : '-'; 
		        case Keys.OemOpenBrackets:  return a_bShift ? '{' : '['; 
		        case Keys.OemPeriod:        return a_bShift ? '>' : '.'; 
		        case Keys.OemPipe:          return a_bShift ? '|' : '\\';
		        case Keys.OemPlus:          return a_bShift ? '+' : '='; 
		        case Keys.OemQuestion:      return a_bShift ? '?' : '/'; 
		        case Keys.OemQuotes:        return a_bShift ? '"' : '\'';
		        case Keys.OemSemicolon:     return a_bShift ? ':' : ';'; 
		        case Keys.OemTilde:         return a_bShift ? '~' : '`'; 
	        }
    		
	        return (char)0;
        }

	    public static char TranslateAlphabetic(char a_Char, bool a_bShift, bool a_bCapsLock)
	    {
		    return (a_bCapsLock ^ a_bShift) ? char.ToUpper(a_Char) : a_Char;
	    }

        public bool KeyWasJustPressed(Keys a_Key)
        {
            if (m_aNewKeys != null && m_aNewKeys.Length > 0)
            {
                for (int i = 0; i < m_aNewKeys.Length; i++)
                {
                    if (m_aNewKeys[i] == a_Key)
                        return true;
                }
            }
            return false;
        }

        public bool KeyWasPressed(Keys a_Key)
        {
            return m_LastKeyState.IsKeyDown(a_Key);
        }
    }
}
